﻿using System.Collections.Generic;
using System.Linq;
using System.IO;
using Newtonsoft.Json;
using System.Threading.Tasks;
using EnvDTE;
using QuickShare.Vsix.SqlList.Forms;
using EnvDTE80;
using System;
using Microsoft.VisualStudio.Shell;

namespace QuickShare.Vsix.SqlList.Commons
{
    public static class DTEHelper
    {
        internal const string SQL_LIST_DIR = ".sqllist";
        internal const string SCRIPT_ROOT = "SqlScripts";
        internal const string SCRIPT_LIST_FILE_NAME = "ScriptList.json";
        internal const string CONFIG_FILE = "deploypath.json";

        public static async Task<Result> AddToListAsync(string projectPath, DTE2 dte2, Project project, List<string> items)
        {
            var rootPath = $"{projectPath}\\{SCRIPT_ROOT}\\";
            var jsonPath = Path.Combine(rootPath, SCRIPT_LIST_FILE_NAME);

            var files = items.Where(x => x.StartsWith(rootPath));
            items = files.Select(x => x.Replace(rootPath, "").Replace("\\", "/")).ToList();
            if(items.Count == 0)
            {
                var msg = $"只能添加项目中~/{SCRIPT_ROOT}文件夹内的脚本文件";
                await dte2.WriteInConsole(msg);
                return Result.Fail(msg);
            }

            var getrst = await GetAsync(jsonPath);
            await dte2.WriteInConsole(getrst.Message);

            await DTEHelper.CheckOutAsync(files);
            await DTEHelper.AddAsync(files);

            List<ScriptItem> list = new List<ScriptItem>();
            var isNewFile = false;
            if(File.Exists(jsonPath))
            {
                string jsonString2 = File.ReadAllText(jsonPath);
                if(!string.IsNullOrWhiteSpace(jsonString2))
                {
                    list = Helper.TryParseFromJson<List<ScriptItem>>(jsonString2);
                }
                var rstj = await CheckOutAsync(jsonPath);
                await dte2.WriteInConsole(rstj.Message);
                if(!rstj.IsSuccess)
                {
                    return rstj;
                }
            }
            else
            {
                isNewFile = true;
            }

            int version;
            foreach(string item in items)
            {
                version = 1;
                ScriptItem exsist = list.FirstOrDefault((ScriptItem x) => x.FilePath == item);
                if(exsist != null)
                {
                    version = ++exsist.Version;
                }
                else
                {
                    list.Add(new ScriptItem {
                        FilePath = item,
                        Version = version
                    });
                }
                var filepath = Path.Combine(rootPath, item);
                AddVerionInfo(filepath, version);
                await dte2.WriteInConsole($"添加/更新脚本: {item} 版本：V{version}");
            }
            await dte2.WriteInConsole($"添加/更新 {items.Count} 个脚本到执行列表");

            list = list.OrderBy(x => x.FilePath).ToList();
            var jsonString = JsonConvert.SerializeObject(list);
            jsonString = Helper.FormatJson(jsonString);
            File.WriteAllText(jsonPath, jsonString);
            if(isNewFile)
            {
                await ThreadHelper.JoinableTaskFactory.SwitchToMainThreadAsync();
                project.ProjectItems.AddFromFile(jsonPath);
                project.Save();
                await dte2.WriteInConsole($"添加项目文件：{SCRIPT_ROOT}/{SCRIPT_LIST_FILE_NAME}");
            }

            return Result.OK($"添加/更新 {items.Count} 个脚本到执行列表");
        }

        private static void AddVerionInfo(string path, int version)
        {
            if(File.Exists(path) && !Helper.IsReadonly(path))
            {
                var encoding = Helper.GetEncoding(path);
                var lines = File.ReadAllLines(path, encoding).ToList();
                lines.Insert(0, $"-- version:{version} at {DateTime.Now:yyyy-MM-dd HH:mm:ss}");
                File.WriteAllLines(path, lines, encoding);
            }
        }

        public static List<string> GetExts(DTE2 dte2, Project project)
        {
            ThreadHelper.ThrowIfNotOnUIThread();
            var solutionPath = Path.GetDirectoryName(dte2.Solution.FullName);
            var projectName = Path.GetFileNameWithoutExtension(project.FullName);
            var dpRst = TryReadDeployConfig(solutionPath, projectName, false);
            if(dpRst != null && !string.IsNullOrWhiteSpace(dpRst.StaticFileExts))
            {
                var list = dpRst.StaticFileExts.Split(";".ToCharArray(), System.StringSplitOptions.RemoveEmptyEntries)
                    .Select(x => "." + x.Trim('.')).ToList();
                return list;
            }
            return new List<string>() { ".html", ".js", ".css" };
        }

        public static string GetProjectRootPath(DTE2 dte2, Project project)
        {
            ThreadHelper.ThrowIfNotOnUIThread();
            var solutionPath = Path.GetDirectoryName(dte2.Solution.FullName);
            var projectName = Path.GetFileNameWithoutExtension(project.FullName);
            var dpRst = TryReadDeployConfig(solutionPath, projectName, false);
            var projectPath = Path.GetDirectoryName(project.FullName);
            if(dpRst != null && !string.IsNullOrWhiteSpace(dpRst.ProjectRootPath))
            {
                var root = (dpRst.ProjectRootPath ?? "").Trim();
                return Path.Combine(projectPath, root);
            }
            else
            {
                return projectPath;
            }
        }
        public static async Task<Result> PushAsync(string solutionPath, string projectPath, string projectName, DTE2 dte2, Project project)
        {
            var rootPath = $"{projectPath}\\{SCRIPT_ROOT}\\";

            var dpRst = TryReadDeployConfig(solutionPath, projectName);
            if(dpRst == null)
            {
                var msg = "未配置发布目录";
                await dte2.WriteInConsole(msg);
                return Result.Fail(msg);
            }

            var jsonPath = Path.Combine(rootPath, SCRIPT_LIST_FILE_NAME);
            List<ScriptItem> list = new List<ScriptItem>();
            if(File.Exists(jsonPath))
            {
                var jsonString = File.ReadAllText(jsonPath);
                if(!string.IsNullOrWhiteSpace(jsonString))
                {
                    list = Helper.TryParseFromJson<List<ScriptItem>>(jsonString);
                }
            }

            if(list.Count == 0)
            {
                await dte2.WriteInConsole("执行列表中没有任何脚本文件");
                return Result.Fail("执行列表中没有任何脚本文件");
            }

            var depPath = Path.Combine(dpRst.Path, SCRIPT_ROOT);
            Helper.TryCreateDir(depPath);
            var getrst = await GetDirAsync(depPath);
            await dte2.WriteInConsole(getrst.Message);

            var jsonPath2 = Path.Combine(depPath, SCRIPT_LIST_FILE_NAME);
            List<ScriptItem> list2 = new List<ScriptItem>();
            if(File.Exists(jsonPath2))
            {
                var jsonString2 = File.ReadAllText(jsonPath2);
                if(!string.IsNullOrWhiteSpace(jsonString2))
                {
                    list2 = Helper.TryParseFromJson<List<ScriptItem>>(jsonString2);
                }
            }

            var files = new Dictionary<string, string>();

            foreach(var item in list)
            {
                var itemPath = Path.Combine(rootPath, item.FilePath);
                if(!File.Exists(itemPath))
                {
                    await dte2.WriteInConsole($"文件不存在：{item.FilePath}");
                    continue;
                }
                ScriptItem exsist = list2.FirstOrDefault((ScriptItem x) => x.FilePath == item.FilePath);
                if(exsist != null)
                {
                    if(exsist.Version >= item.Version)
                    {
                        continue;
                    }
                    exsist.Version = item.Version;
                }

                var itemPath2 = Path.Combine(depPath, item.FilePath);
                files.Add(itemPath, itemPath2);
            }
            if(files.Count == 0)
            {
                await dte2.WriteInConsole("执行列表中没有需要推送的脚本文件");
                return Result.Fail("执行列表中没有需要推送的脚本文件");
            }
            files.Add(jsonPath, jsonPath2);

            var dirs = files.Values.Select(x => Path.GetDirectoryName(x)).Distinct().ToList();
            dirs.ForEach(Helper.TryCreateDir);
            await CheckOutAsync(files.Values);

            foreach(var item in files)
            {
                Helper.RemoveReadonly(item.Value);
                File.Copy(item.Key, item.Value, true);
                var fff = item.Value.Replace(depPath, "");
                await dte2.WriteInConsole($"推送脚本：{fff}");
            }
            await AddAsync(files.Values);

            var message = $"推送脚本文件 {files.Count - 1} 个";
            await dte2.WriteInConsole(message);
            return Result.OK(message);
        }
        //public static async Task<Result> PushOldAsync(DTE2 dte2, Project project)
        //{
        //    await ThreadHelper.JoinableTaskFactory.SwitchToMainThreadAsync();

        //    var solutionPath = Path.GetDirectoryName(dte2.Solution.FullName);
        //    var projectPath = Path.GetDirectoryName(project.FullName);
        //    var projectName = Path.GetFileNameWithoutExtension(project.FullName);
        //    var rootPath = $"{projectPath}\\{SCRIPT_ROOT}\\";

        //    var dpRst = TryReadDeployConfig(solutionPath, projectName);
        //    if(dpRst == null)
        //    {
        //        var msg = "未配置发布目录";
        //        dte2.WriteInConsole(msg);
        //        return Result.Fail(msg);
        //    }

        //    var jsonPath = Path.Combine(rootPath, SCRIPT_LIST_FILE_NAME);
        //    List<ScriptItem> list = new List<ScriptItem>();
        //    if(File.Exists(jsonPath))
        //    {
        //        var jsonString = File.ReadAllText(jsonPath);
        //        if(!string.IsNullOrWhiteSpace(jsonString))
        //        {
        //            list = Helper.TryParseFromJson<List<ScriptItem>>(jsonString);
        //        }
        //    }

        //    if(list.Count == 0)
        //    {
        //        dte2.WriteInConsole("执行列表中没有任何脚本文件");
        //        return Result.Fail("执行列表中没有任何脚本文件");
        //    }


        //    var depPath = Path.Combine(dpRst.Path, SCRIPT_ROOT);
        //    Helper.TryCreateDir(depPath);

        //    var getrst = await GetDirAsync(depPath);
        //    dte2.WriteInConsole(getrst.Message);

        //    var jsonPath2 = Path.Combine(depPath, SCRIPT_LIST_FILE_NAME);
        //    List<ScriptItem> list2 = new List<ScriptItem>();
        //    if(!File.Exists(jsonPath2))
        //    {
        //        foreach(var item in list)
        //        {
        //            var itemPath = Path.Combine(rootPath, item.FilePath);
        //            var itemPath2 = Path.Combine(depPath, item.FilePath);
        //            var dir = Path.GetDirectoryName(itemPath2);
        //            Helper.TryCreateDir(dir);
        //            File.Copy(itemPath, itemPath2, true);
        //        }
        //        File.Copy(jsonPath, jsonPath2, true);
        //        var addRst = await AddDirAsync(depPath);
        //        dte2.WriteInConsole(addRst.Message);
        //        return Result.OK();
        //    }
        //    var jsonString2 = File.ReadAllText(jsonPath2);
        //    if(!string.IsNullOrWhiteSpace(jsonString2))
        //    {
        //        list2 = Helper.TryParseFromJson<List<ScriptItem>>(jsonString2);
        //    }
        //    var expectCount = 0;
        //    var successCount = 0;

        //    foreach(var item in list)
        //    {
        //        var itemPath = Path.Combine(rootPath, item.FilePath);
        //        if(!File.Exists(itemPath))
        //        {
        //            dte2.WriteInConsole($"文件不存在：{itemPath}");
        //            continue;
        //        }
        //        ScriptItem exsist = list2.FirstOrDefault((ScriptItem x) => x.FilePath == item.FilePath);
        //        if(exsist != null)
        //        {
        //            if(exsist.Version >= item.Version)
        //            {
        //                continue;
        //            }
        //            exsist.Version = item.Version;
        //        }
        //        else
        //        {
        //            list2.Add(item);
        //        }
        //        var itemPath2 = Path.Combine(depPath, item.FilePath);
        //        var dir = Path.GetDirectoryName(itemPath2);
        //        Helper.TryCreateDir(dir);
        //        expectCount++;
        //        var rstitem = await CheckOutOrAddAsync(itemPath2, itemPath);
        //        dte2.WriteInConsole(rstitem.Message);
        //        if(rstitem.IsSuccess)
        //        {
        //            successCount++;
        //        }
        //        else
        //        {
        //            return rstitem;
        //        }
        //    }

        //    //list2 = list2.OrderBy(x => x.FilePath).ToList();
        //    //var jsonString2 = JsonConvert.SerializeObject(list2);
        //    //jsonString2 = FormatJson(jsonString2);
        //    var rst = await CheckOutOrAddAsync(jsonPath2, jsonPath);
        //    if(!rst.IsSuccess)
        //    {
        //        return rst;
        //    }
        //    dte2.WriteInConsole(rst.Message);

        //    var message = $"预计推送脚本文件{expectCount}，推送成功{successCount}";
        //    dte2.WriteInConsole(message);
        //    return Result.OK();
        //}
        //public static async Task<Result> PushStaticFilesOldAsync(DTE2 dte2, Project project, List<string> items)
        //{
        //    await ThreadHelper.JoinableTaskFactory.SwitchToMainThreadAsync();

        //    var solutionPath = Path.GetDirectoryName(dte2.Solution.FullName);
        //    var projectPath = Path.GetDirectoryName(project.FullName) + "\\";
        //    var projectName = Path.GetFileNameWithoutExtension(project.FullName);

        //    items = items.Where(x => x.StartsWith(projectPath)).ToList();
        //    if(items.Count == 0)
        //    {
        //        var msg = $"只能推送项目中的静态资源文件";
        //        dte2.WriteInConsole(msg);
        //        return Result.Fail(msg);
        //    }

        //    var dpRst = TryReadDeployConfig(solutionPath, projectName);
        //    if(dpRst == null)
        //    {
        //        var msg = "未配置发布目录";
        //        dte2.WriteInConsole(msg);
        //        return Result.Fail(msg);
        //    }

        //    var depPath = dpRst.Path.Trim('\\', '/') + "\\";

        //    var expectCount = 0;
        //    var successCount = 0;

        //    if(string.IsNullOrWhiteSpace(dpRst.ProjectRootPath))
        //    {
        //        projectPath = Path.Combine(projectPath, dpRst.ProjectRootPath);
        //    }

        //    foreach(var itemPath in items)
        //    {
        //        if(!File.Exists(itemPath))
        //        {
        //            dte2.WriteInConsole($"文件不存在：{itemPath}");
        //            continue;
        //        }

        //        var itemPath2 = itemPath.Replace(projectPath, depPath);
        //        var dir = Path.GetDirectoryName(itemPath2);
        //        Helper.TryCreateDir(dir);
        //        expectCount++;
        //        var rstitem = await CheckOutOrAddAsync(itemPath2, itemPath);
        //        dte2.WriteInConsole(rstitem.Message);
        //        if(rstitem.IsSuccess)
        //        {
        //            successCount++;
        //        }
        //        else
        //        {
        //            return rstitem;
        //        }
        //    }

        //    var message = $"预计推送静态资源文件{expectCount}，推送成功{successCount}";
        //    dte2.WriteInConsole(message);
        //    return Result.OK();
        //}
        public static async Task<Result> PushStaticFilesAsync(DTE2 dte2, Project project, List<string> items)
        {
            await ThreadHelper.JoinableTaskFactory.SwitchToMainThreadAsync();

            var solutionPath = Path.GetDirectoryName(dte2.Solution.FullName);
            var projectPath = Path.GetDirectoryName(project.FullName) + "\\";
            var projectName = Path.GetFileNameWithoutExtension(project.FullName);

            items = items.Where(x => x.StartsWith(projectPath)).ToList();
            if(items.Count == 0)
            {
                var msg = $"只能推送项目中的静态资源文件";
                await dte2.WriteInConsole(msg);
                return Result.Fail(msg);
            }

            var dpRst = TryReadDeployConfig(solutionPath, projectName);
            if(dpRst == null)
            {
                var msg = "未配置发布目录";
                await dte2.WriteInConsole(msg);
                return Result.Fail(msg);
            }

            var depPath = dpRst.Path.Trim('\\', '/') + "\\";
            Helper.TryCreateDir(depPath);
            var getrst = await GetDirAsync(depPath);
            await dte2.WriteInConsole(getrst.Message);

            if(!string.IsNullOrWhiteSpace(dpRst.ProjectRootPath))
            {
                projectPath = Path.Combine(projectPath, dpRst.ProjectRootPath);
            }

            var files = items.Where(x => File.Exists(x)).ToDictionary(x => x, x => x.Replace(projectPath, depPath));
            var dirs = files.Values.Select(x => Path.GetDirectoryName(x)).Distinct().ToList();
            dirs.ForEach(Helper.TryCreateDir);
            await CheckOutAsync(files.Values);

            foreach(var item in files)
            {
                Helper.RemoveReadonly(item.Value);
                File.Copy(item.Key, item.Value, true);
                var fff = item.Value.Replace(depPath, "");
                await dte2.WriteInConsole($"推送文件：{fff}");
            }
            await AddAsync(files.Values);

            var message = $"预计推送静态资源文件 {items.Count} 个，推送成功 {files.Count} 个";
            await dte2.WriteInConsole(message);
            return Result.OK(message);
        }

        private static async Task<Result> CheckOutAsync(string path)
        {
            if(Helper.IsReadonly(path))
            {
                var rst = await Command.RunAsync("tf", $"vc checkout \"{path}\" /lock:checkout");
                if(rst.Status != CommandStatus.Success)
                {
                    if(rst.Message.Contains("$/"))
                    {
                        return Result.Fail(rst.Message);
                    }
                    Helper.RemoveReadonly(path);
                }
                else
                {
                    return Result.OK($"签出并修改文件 {rst.Message}");
                }
            }
            return Result.OK();
        }

        private static async Task<Result> GetAsync(string path)
        {
            var rst = await Command.RunAsync("tf", $"vc get \"{path}\"");
            if(rst.Status != CommandStatus.Success)
            {
                if(rst.Message.Contains("tf workspaces"))
                {
                    Helper.RemoveReadonly(path);
                    return Result.OK();
                }
                else
                {
                    return Result.Fail(rst.Message);
                }
            }
            else
            {
                return Result.OK($"获取文件 {path} 完成 {rst.Message}");
            }
        }
        private static async Task<Result> GetDirAsync(string dirPath)
        {
            var rst = await Command.RunAsync("tf", $"vc get \"{dirPath}\" /recursive");
            if(rst.Status != CommandStatus.Success)
            {
                if(rst.Message.Contains("tf workspaces"))
                {
                    return Result.OK();
                }
                else
                {
                    return Result.Fail(rst.Message);
                }
            }
            else
            {
                return Result.OK($"获取文件夹 {dirPath} 完成 {rst.Message}");
            }
        }
        private static async Task<Result> AddDirAsync(string dirPath)
        {
            var rst = await Command.RunAsync("tf", $"vc add \"{dirPath}\" /recursive");
            if(rst.Status != CommandStatus.Success)
            {
                if(rst.Message.Contains("tf workspaces"))
                {
                    return Result.OK();
                }
                else
                {
                    return Result.Fail(rst.Message);
                }
            }
            else
            {
                return Result.OK($"添加文件夹 {dirPath} 完成 {rst.Message}");
            }
        }

        //private static async Task<Result> CheckOutOrAddAsync(List<(string, string)> paths)
        //{
        //    var tfcPaths = string.Join(" ", paths.Select(x => x.Item1));
        //    var rst = await Command.RunAsync("tf", $"vc checkout \"{tfcPaths}\" /lock:checkout");

        //}

        private static async Task<Result> CheckOutOrAddAsync(string path, string sourcePath)
        {
            var rst = await Command.RunAsync("tf", $"vc checkout \"{path}\" /lock:checkout");
            if(rst.Status == CommandStatus.Success)
            {
                File.Copy(sourcePath, path, true);
                return Result.OK($"签出并修改文件 {rst.Message}");
            }
            if(rst.Status != CommandStatus.Fail)
            {
                return Result.OK();
            }
            if(rst.Message.Contains("$/"))
            {
                return Result.Fail(rst.Message);
            }
            Helper.RemoveReadonly(path);
            File.Copy(sourcePath, path, true);
            if(rst.Message.Contains("tf workspaces"))
            {
                return Result.OK($"修改文件 {path}");
            }
            var rst2 = await Command.RunAsync("tf", $"vc add \"{path}\" /lock:checkout");
            if(rst2.Status == CommandStatus.Success)
            {
                return Result.OK($"添加文件 {path} 成功 {rst2.Message}");
            }
            return Result.Fail(rst2.Message);
        }

        public static async System.Threading.Tasks.Task CheckOutAsync(IEnumerable<string> items)
        {
            var paths = string.Join(" ", items.Select(x => $"\"{x}\""));
            await Command.RunAsync("tf", $"vc checkout {paths} /lock:checkout");
        }
        public static async System.Threading.Tasks.Task AddAsync(IEnumerable<string> items)
        {
            var paths = string.Join(" ", items.Select(x => $"\"{x}\""));
            await Command.RunAsync("tf", $"vc add {paths} /lock:checkout");
        }


        private static DeployInfo TryReadDeployConfig(string solutionPath, string projectName, bool show = true)
        {
            var sqllistdir = TryCreateSqlListDir(solutionPath, projectName);
            var path = Path.Combine(sqllistdir, CONFIG_FILE);
            string message = null;
            if(File.Exists(path))
            {
                var text = File.ReadAllText(path);
                if(!string.IsNullOrWhiteSpace(text))
                {
                    var dp = Helper.TryParseFromJson<DeployInfo>(text);
                    if(dp != null && !string.IsNullOrWhiteSpace(dp.Path))
                    {
                        if(Directory.Exists(dp.Path))
                        {
                            return dp;
                        }
                        else
                        {
                            message = "配置的发布目录不存在";
                        }
                    }
                    else
                    {
                        message = "配置的发布目录不正确";
                    }
                }
                else
                {
                    message = "配置的发布目录不正确";
                }
            }
            if(show)
            {
                var frmDeploy = new FrmDeployInfos(projectName, path, message);
                var da = frmDeploy.ShowDialog();
                if(da != System.Windows.Forms.DialogResult.OK)
                {
                    return null;
                }
                return frmDeploy.Value;
            }
            return null;
        }
        public static string TryCreateSqlListDir(string solutionPath, string projectName)
        {
            var sqlListPath = Path.Combine(solutionPath, SQL_LIST_DIR);
            Helper.TryCreateDir(sqlListPath);
            var attrs = File.GetAttributes(sqlListPath);
            if(!attrs.HasFlag(FileAttributes.Hidden))
            {
                attrs = attrs | FileAttributes.Hidden;
                File.SetAttributes(sqlListPath, attrs);
            }
            var targetPath = Path.Combine(sqlListPath, projectName);
            return targetPath;
        }
    }
}
